package com.ynet.middleground.contract.model;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ynet.core.common.BaseReqObj;
import com.ynet.core.exception.BusinessException;
import com.ynet.middleground.contract.bean.*;
import com.ynet.middleground.contract.constant.SystemConsts;
import com.ynet.middleground.contract.constant.TemplateStatusEnum;
import com.ynet.middleground.contract.dao.TemplateTableParamMapper;
import com.ynet.middleground.contract.dto.TemplateTableParamDTO;
import com.ynet.middleground.contract.entity.Template;
import com.ynet.middleground.contract.entity.TemplateParam;
import com.ynet.middleground.contract.entity.TemplateTableParam;
import com.ynet.middleground.contract.utils.BeanUtil;

/**
 * @author liulx
 * @description 模板中关于表格具体列参数的管理
 * @date 2020-04-03 14:13
 */
@Component
public class TemplateTableParamModel extends ServiceImpl<TemplateTableParamMapper, TemplateTableParam> {

    @Autowired
    private Mapper mapper;

    @Autowired
    private TemplateModel templateModel;

    @Autowired
    private TemplateParamModel templateParamModel;

    @Autowired
    private UserCenterModel userCenterModel;

    /**
     * 根据表格 id 查询表格单元格参数列表
     *
     * @param req 查询模板表格的参数信息请求类
     * @return 表格单元格参数列表
     */
    public List<TemplateTableParamDTO> listTemplateTableParamsByTableId(QueryTemplateTableParamsReq req) {
        List<TemplateTableParam> templateTableParams = listTemplateTableParamsByTableId(req.getTableId(), req, null);
        return BeanUtil.mapList(mapper, templateTableParams, TemplateTableParamDTO.class);
    }

    /**
     * 根据表格 id 查询表格单元格参数列表
     *
     * @param tableId 表格 id
     * @param req 基础请求对象
     * @return 表格单元格参数列表
     */
    public List<TemplateTableParam> listTemplateTableParamsByTableId(Integer tableId, BaseReqObj req,
        Integer formatId) {
        LambdaQueryWrapper<TemplateTableParam> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TemplateTableParam::getTableId, tableId);
        queryWrapper.eq(TemplateTableParam::getChannel, req.getChannel());
        queryWrapper.eq(TemplateTableParam::getIsDeleted, SystemConsts.NOT_DELETED);
        if (null != formatId) {
            queryWrapper.eq(TemplateTableParam::getFormatId, formatId);
        }
        return this.list(queryWrapper);
    }

    /**
     * 添加表格单元格参数
     *
     * @param req 新增模板表格单元格参数请求类
     */
    public void addTemplateTableParams(AddTemplateTableParamsReq req) {
        Integer tableId = req.getTableId();
        List<TemplateTableParamReq> params = req.getParams();
        if (params == null || params.isEmpty()) {
            throw new BusinessException("表格参数列表不能为空", "ECCT0001");
        }
        // 检查表格参数是否有重名
        List<String> namesList = checkRepeatParams(params);
        // 查询表格是否有效
        TemplateParam tableParam = templateParamModel.getById(tableId);
        if (tableParam == null || tableParam.getIsDeleted()) {
            throw new BusinessException("模板参数不存在", "ECCT0202");
        }
        Template template = templateModel.getById(tableParam.getTemplateId());
        Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));
        if (TemplateStatusEnum.EXPIRE.getCode().equals(template.getStatus())) {
            throw new BusinessException("请使用当前合约模板最新版本", "ECCT0105");
        }

        // add by liwq on 20220921 start
        // 模板已关联业态
        if (template.getFormatId() != null && req.getFormatId() == null) {
            throw new BusinessException("该模板已关联业态信息，请上送所属业态id！", "ECCT0009");
        }
        // 新增时上送的业态与模板关联业态的不一致，则不允许新增
        if (null != req.getFormatId() && !req.getFormatId().equals(template.getFormatId())) {
            throw new BusinessException("该模板关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }
        // add by liwq on 20220921 end

        // 检查表格参数对应的模板是否有效
        template = templateModel.getOnlyLastestTemplate(tableParam.getTemplateId(), req);
        if (!TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
            && !TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
            throw new BusinessException("模板状态为“未启用”或“已停用”才可新增参数", "ECCT0104");
        }
        // 查询是否与已经存在的表格参数重复并封装新增表格参数其余信息
        List<TemplateTableParam> addParamList = convertTableParam(tableId, namesList, params, req);

        // 如果是已停用状态模板，需要保存历史记录
        if (TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
            LambdaQueryWrapper<TemplateTableParam> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TemplateTableParam::getTableId, tableId);
            queryWrapper.eq(TemplateTableParam::getIsDeleted, SystemConsts.NOT_DELETED);
            // 查询出当前模板参数信息，一并与新增加的参数作为最新参数列表
            List<TemplateTableParam> sourceParamList = this.list(queryWrapper);
            addParamList.addAll(sourceParamList);
            // 保存历史模板
            templateModel.saveHistoryRecord(template, tableId, addParamList, req, req.getFormatId());
            return;
        }
        this.saveBatch(addParamList);
    }

    /**
     * 封装表格参数对象并检查是否重复
     *
     * @param tableId 表格 id
     * @param namesList 参数名称集合
     * @param reqParams 请求时的表格参数集合
     * @param req 基础请求信息对象
     * @return 封装后的表格参数集合
     */
    public List<TemplateTableParam> convertTableParam(Integer tableId, List<String> namesList,
        List<TemplateTableParamReq> reqParams, BaseReqObj req) {
        // 查询是否与已经存在的表格参数重复
        LambdaQueryWrapper<TemplateTableParam> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TemplateTableParam::getTableId, tableId);
        queryWrapper.eq(TemplateTableParam::getIsDeleted, SystemConsts.NOT_DELETED);
        queryWrapper.in(TemplateTableParam::getName, namesList);
        List<TemplateTableParam> repeatParams = this.list(queryWrapper);
        if (repeatParams != null && !repeatParams.isEmpty()) {
            StringBuilder finalRepeatMsg = new StringBuilder("重复参数名：");
            repeatParams.forEach(param -> {
                finalRepeatMsg.append(param.getName());
                finalRepeatMsg.append("，");
            });
            finalRepeatMsg.append("请检查");
            throw new BusinessException(finalRepeatMsg.toString(), "ECCT0200");
        }
        // 封装新增表格参数其余信息
        return reqParams.stream().map(reqParam -> {
            TemplateTableParam param = mapper.map(reqParam, TemplateTableParam.class);
            param.setTableId(tableId);
            param.setChannel(req.getChannel());
            param.setCreateBy(req.getOperationUserId());
            param.setGmtCreate(LocalDateTime.now());
            param.setModifiedBy(req.getOperationUserId());
            param.setGmtModified(LocalDateTime.now());

            Integer formatId = null;
            if (req instanceof AddTemplateTableParamsReq) {
                formatId = ((AddTemplateTableParamsReq)req).getFormatId();
            }
            if (req instanceof AddTemplateParamsReq) {
                formatId = ((AddTemplateParamsReq)req).getFormatId();
            }
            if (req instanceof AddTemplateReq) {
                formatId = ((AddTemplateReq)req).getFormatId();
            }
            param.setFormatId(formatId);
            return param;
        }).collect(Collectors.toList());
    }

    /**
     * 根据表格参数 id 修改参数信息
     *
     * @param req 修改模板表格参数信息请求类
     * @param isDelete 是否是删除操作
     */
    public void updateTemplateTableParamById(UpdateTemplateTableParamReq req, boolean isDelete) {
        TemplateTableParam tableParam = this.getById(req.getId());
        if (tableParam == null || tableParam.getIsDeleted()) {
            throw new BusinessException("模板表格参数不存在", "ECCT0202");
        }
        req.setIsDeleted(isDelete);
        // 查询表格是否有效
        TemplateParam table = templateParamModel.getById(tableParam.getTableId());
        if (table == null || table.getIsDeleted()) {
            throw new BusinessException("模板参数不存在", "ECCT0202");
        }
        // 检查表格参数对应的模板是否有效
        Template template = templateModel.getById(table.getTemplateId());
        Optional.ofNullable(template).orElseThrow(() -> new BusinessException("合约模板不存在", "ECCT0100"));
        if (TemplateStatusEnum.EXPIRE.getCode().equals(template.getStatus())) {
            throw new BusinessException("请使用当前合约模板最新版本", "ECCT0105");
        }

        // add by liwq on 20220921 start
        // 模板已关联业态，修改时却未上送业态，则不允许修改
        if (template.getFormatId() != null && req.getFormatId() == null) {
            throw new BusinessException("该模板已关联业态信息，请上送所属业态id！", "ECCT0009");
        }
        // 修改时上送的业态与模板关联业态的不一致，则不允许修改
        if (null != req.getFormatId() && !req.getFormatId().equals(template.getFormatId())) {
            throw new BusinessException("该模板关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }
        // add by liwq on 20220921 end

        template = templateModel.getOnlyLastestTemplate(table.getTemplateId(), req);
        if (!TemplateStatusEnum.NOT_ACTIVATE.getCode().equals(template.getStatus())
            && !TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
            throw new BusinessException("模板状态为“未启用”或“已停用”才可调整参数", "ECCT0104");
        }
        // 查询是否与已经存在的表格参数重复
        LambdaQueryWrapper<TemplateTableParam> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TemplateTableParam::getTableId, tableParam.getTableId());
        queryWrapper.eq(TemplateTableParam::getName, req.getName());
        queryWrapper.eq(TemplateTableParam::getIsDeleted, SystemConsts.NOT_DELETED);
        TemplateTableParam existParam = this.getOne(queryWrapper);
        if (existParam != null && !existParam.getId().equals(req.getId())) {
            throw new BusinessException("重复参数名：" + req.getName() + "，请检查", "ECCT0200");
        }
        mapper.map(req, tableParam);
        // 如果是已停用状态模板，需要保存历史记录
        if (TemplateStatusEnum.DISABLED.getCode().equals(template.getStatus())) {
            queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TemplateTableParam::getIsDeleted, SystemConsts.NOT_DELETED);
            queryWrapper.eq(TemplateTableParam::getTableId, tableParam.getTableId());
            // 查询出当前模板参数信息，一并与新增加的参数作为最新参数列表
            List<TemplateTableParam> sourceParamList = this.list(queryWrapper);
            List<TemplateTableParam> newParamList = sourceParamList.stream().map(param -> {
                if (param.getId().equals(tableParam.getId())) {
                    return tableParam;
                }
                return param;
            }).collect(Collectors.toList());
            // 保存历史模板
            templateModel.saveHistoryRecord(template, tableParam.getTableId(), newParamList, req, req.getFormatId());
            return;
        }
        this.updateById(tableParam);

    }

    /**
     * 删除模板的表格中指定参数信息
     *
     * @param req 删除模板表格参数请求类
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteTemplateTableParam(DeleteTemplateTableParamReq req) {
        TemplateTableParam tableParam = this.getById(req.getId());
        Optional.ofNullable(tableParam).orElseThrow(() -> new BusinessException("模板参数不存在", "ECCT0202"));

        // add by liwq on 20220921 start
        // 表格参数已关联业态，修改时却未上送业态，则不允许修改
        if (tableParam.getFormatId() != null && req.getFormatId() == null) {
            throw new BusinessException("该表格参数已关联业态信息，请上送所属业态id！", "ECCT0009");
        }
        // 修改时上送的业态与表格参数关联业态的不一致，则不允许修改
        if (null != req.getFormatId() && !req.getFormatId().equals(tableParam.getFormatId())) {
            throw new BusinessException("该表格参数关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }
        // add by liwq on 20220921 end

        UpdateTemplateTableParamReq updateTemplateTableParamReq = new UpdateTemplateTableParamReq();
        mapper.map(tableParam, updateTemplateTableParamReq);
        updateTemplateTableParamReq.setId(req.getId());
        updateTemplateTableParamReq.setChannel(req.getChannel());
        updateTemplateTableParamReq.setOperationUserId(req.getOperationUserId());
        updateTemplateTableParamReq.setTraceId(req.getTraceId());
        updateTemplateTableParamReq.setRequestTime(req.getRequestTime());
        updateTemplateTableParamReq.setIsDeleted(true);
        updateTemplateTableParamById(updateTemplateTableParamReq, true);
    }

    /**
     * 检查模板参数是否有重名
     *
     * @param params 模板参数列表
     * @return 不重名则返回模板参数名称列表
     */
    public List<String> checkRepeatParams(List<TemplateTableParamReq> params) {
        List<String> namesList = params.stream().map(TemplateTableParamReq::getName).collect(Collectors.toList());
        List<String> repeatNames = BeanUtil.getDuplicateElements(namesList);
        if (repeatNames != null && !repeatNames.isEmpty()) {
            StringBuilder repeatMsg = new StringBuilder("重复参数名：");
            repeatNames.forEach(param -> {
                repeatMsg.append(param);
                repeatMsg.append("，");
            });
            repeatMsg.append("请检查");
            throw new BusinessException(repeatMsg.toString(), "ECCT0200");
        }
        return namesList;
    }

    /**
     * 根据模板表格 id 删除表格参数
     *
     * @param tableId 模板表格 id
     * @param baseReqObj 基础请求参数
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteTeplateTableParamsByTableId(Integer tableId, BaseReqObj baseReqObj) {
        LambdaQueryWrapper<TemplateTableParam> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TemplateTableParam::getTableId, tableId);
        queryWrapper.eq(TemplateTableParam::getIsDeleted, SystemConsts.NOT_DELETED);
        List<TemplateTableParam> params = this.list(queryWrapper);
        if (params.isEmpty()) {
            return;
        }
        List<TemplateTableParam> updateParamList = params.stream().peek(param -> {
            param.setIsDeleted(true);
            param.setModifiedBy(baseReqObj.getOperationUserId());
            param.setGmtModified(LocalDateTime.now());
        }).collect(Collectors.toList());
        this.updateBatchById(updateParamList);
    }

}
